package com.miracle.flexAPI.api.service.impl;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.miracle.utils.ConstantsFlexApi;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.common.utils.uuid.UUID;
import com.miracle.flexAPI.api.mapper.SysTaskColumnsMapper;
import com.miracle.flexAPI.api.entity.SysCacheConfig;
import com.miracle.flexAPI.api.entity.SysTask;
import com.miracle.flexAPI.api.mapper.SysTaskMapper;
import com.miracle.flexAPI.api.entity.SysTaskColumn;
import com.miracle.flexAPI.api.service.TaskManageService;
import com.miracle.utils.DatabaseToolTaskManage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 任务管理
 *
 * @author miracle
 * @since 2021-08-23 16:05:08
 */
@Service
@Slf4j
public class TaskManageServiceImpl implements TaskManageService {

    @Resource
    private SysTaskMapper sysTaskMapper;

    @Resource
    private SysTaskColumnsMapper sysTaskColumnsMapper;

    @Resource
    DynamicRoutingDataSource dynamicRoutingDataSource;

    @Resource
    RedisCache redisCache;


    /**
     * 查询列表
     *
     * @param model 实例对象
     * @return 对象列表
     */
    @Override
    public AjaxResult list(SysTask model) {
        List<SysTask> list = this.sysTaskMapper.queryByDto(model);
        return AjaxResult.success(list);
    }

    /**
     * 根据ID查询任务
     *
     * @param taskId ID
     * @return 对象列表
     */
    @Override
    public SysTask findById(String taskId) {
        SysTask model = new SysTask();
        model.setTaskId(taskId);
        List<SysTask> list = sysTaskMapper.queryByDto(model);
        if(list.size() > 0){
            return list.get(0);
        }
        return null;
    }

    /**
     * 新增数据
     *
     * @param model 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = {Exception.class})
    public AjaxResult add(SysTask model) throws Exception {

        String taskId = "";
        if(StringUtils.isBlank(model.getTaskId())){
            taskId = UUID.randomUUIDString();
            model.setTaskId(taskId);
        }else {
            taskId = model.getTaskId();
        }


        DataSource dataSource = dynamicRoutingDataSource;
        if(StringUtils.isNotBlank(model.getTaskSourceId())){
            dataSource = dynamicRoutingDataSource.getDataSource(model.getTaskSourceId());
        }

        //1.保存任务
        int result = this.sysTaskMapper.insertByModel(model);

        //2.获取表头信息
        DatabaseToolTaskManage databaseToolTaskManage = new DatabaseToolTaskManage(dataSource)
                .executeSql(model.getTaskSql(), new HashMap());
        List<SysTaskColumn> sqlColumnList = databaseToolTaskManage.getSqlColumnList(taskId);
        //3.保存表头信息
        for (SysTaskColumn column : sqlColumnList) {
            sysTaskColumnsMapper.insertByModel(column);
        }

        return AjaxResult.success(result);
    }

    /**
     * 修改数据
     *
     * @param model 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = {Exception.class})
    public AjaxResult edit(SysTask model) throws Exception {

        String taskId = model.getTaskId();

        //2.保存任务信息
        int result = this.sysTaskMapper.updateByModel(model);
        //1.获取表头信息
        List<SysTaskColumn> sqlColumnList;

        DataSource dataSource = dynamicRoutingDataSource;
        if(StringUtils.isNotBlank(model.getTaskSourceId())){
            dataSource = dynamicRoutingDataSource.getDataSource(model.getTaskSourceId());
        }

        DatabaseToolTaskManage databaseToolTaskManage = new DatabaseToolTaskManage(dataSource)
                .executeSql(model.getTaskSql(), new HashMap());
        sqlColumnList = databaseToolTaskManage.getSqlColumnList(taskId);

        ////2.保存任务信息
        //int result = this.sysTaskMapper.updateByModel(model);

        //3.获取原表头信息
        List<SysTaskColumn> sysTaskColumnsOld = sysTaskColumnsMapper.queryByID(taskId);

        if(sysTaskColumnsOld.size()>0){
            //4.合并新旧表头
            for (int i = 0; i < sqlColumnList.size(); i++) {
                for (int j = 0; j < sysTaskColumnsOld.size(); j++) {
                    String colNameEng = sqlColumnList.get(i).getColNameEng();
                    String colNameEngOld = sysTaskColumnsOld.get(j).getColNameEng();
                    if (colNameEng.equals(colNameEngOld)){
                        SysTaskColumn tmpColumn = sysTaskColumnsOld.get(j);
                        //把新对象的排序赋值过来
                        sqlColumnList.set(i,tmpColumn);
                        continue;
                    }
                }
            }
        }

        //5.保存表头
        sysTaskColumnsMapper.deleteByModel(taskId);
        for (SysTaskColumn column : sqlColumnList) {
            sysTaskColumnsMapper.insertByModel(column);
        }

        return AjaxResult.success(result);
    }

    /**
     * 禁用数据
     *
     * @param model 实例对象
     * @return 禁用数量
     */
    @Override
    public AjaxResult delete(SysTask model) {
        SysTask realModel = new SysTask();
        realModel.setTaskId(model.getTaskId());
        realModel.setStatus(0);
        int result = this.sysTaskMapper.updateByModelNullable(realModel);
        return AjaxResult.success(result);
    }

    /**
     * 根据ID执行任务
     *
     * @param model 实例对象
     * @return 是否成功
     */
    @Override
    public AjaxResult executeTaskById4Page(SysTask model) {
        if(StringUtils.isNotBlank(model.getTaskId())){
            SysTask newModel = findById(model.getTaskId());
            if(null != newModel){
                newModel.setParams(model.getParams());
                List<Map<String, Object>> dataList = executeTaskByModel(newModel);
                Map<String, Object> data = new HashMap();
                data.put("dataList", dataList);
                data.put("columnList", newModel.getSysTaskColumns());
                return AjaxResult.success(data);
            }
        }
        return null;
    }

    /**
     * 根据任务ID执行任务
     *
     * @param model 实例对象
     * @return
     */
    @Override
    public List<Map<String, Object>> executeTaskById(SysTask model) {
        if(StringUtils.isNotBlank(model.getTaskId())){
            SysTask newModel = findById(model.getTaskId());
            if(null != findById(model.getTaskId())){
                newModel.setParams(model.getParams());
                return executeTaskByModel(newModel);
            }
        }
        return null;
    }

    /**
     * 根据任务实体执行任务
     *
     * @param model 实例对象
     * @return
     */
    @Override
    public List<Map<String, Object>> executeTaskByModel(SysTask model) {
        if (null == model) {
            throw new NullPointerException("SysTask is null");
        }

        // 1. 初始化任务参数
        String datasourceCode = model.getTaskSourceId();//自定义数据源
        DataSource dataSource = StringUtils.isNotBlank(datasourceCode) ? dynamicRoutingDataSource.getDataSource(datasourceCode) : dynamicRoutingDataSource;

        SysCacheConfig cacheConfig = model.getSysCacheConfig();

        boolean cacheAble = StringUtils.isBlank(model.getCacheCode()) ? false : true; //是否开启缓存

        Integer cacheTime = cacheConfig.getCacheTime();//缓存有效期(秒, -1表识永久有效)

        Integer cacheEmptyTime = cacheConfig.getCacheTimeEmpty();//空值缓存有效期（秒, -1表识永久有效）

        String sysTaskKey = ConstantsFlexApi.SYS_TASK_KEY; //缓存默认前缀

        String cacheCustomKey = cacheConfig.getCachePrefix(); //缓存自定义key

        String cacheRealId = Md5Utils.hash(model.toString());//缓存唯一标识，根据参数（dto）生成

        String cacheId = sysTaskKey + cacheCustomKey + cacheRealId;//完整缓存ID

        DatabaseToolTaskManage databaseToolTaskManage = new DatabaseToolTaskManage(dataSource);
        // 2.1 不使用缓存的情况
        if (!cacheAble) {
            try {
                databaseToolTaskManage = databaseToolTaskManage.executeSql(model.getTaskSql(), model.getParams());
                List<Map<String, Object>> sqlResult = databaseToolTaskManage.getSqlResult();
                return sqlResult;
            } catch (SQLException | IllegalAccessException e) {
                log.error("executeTaskByModel occur exception", e);
                throw new RuntimeException(e.getMessage());
            } finally {
                try {
                    if (databaseToolTaskManage != null) {
                        databaseToolTaskManage.close();
                    }
                } catch (SQLException e) {
                    log.error("executeTaskByModel occur exception when close source", e);
                    throw new RuntimeException(e.getMessage());
                }
            }
        }

        // 2.2 使用缓存的情况
        log.debug("cacheId: " + cacheId);
        List<Map<String, Object>> cacheResult = redisCache.getCacheObject(cacheId);
        if (cacheResult != null) {//缓存有数（cacheId 存在，不排除空值）
            return cacheResult;
        }

        //3.执行SQL，查询数据
        try {
            databaseToolTaskManage = databaseToolTaskManage.executeSql(model.getTaskSql(), model.getParams());//执行任务
            List<Map<String, Object>> sqlResult = databaseToolTaskManage.getSqlResult();//查询数据

            //4.缓存数据
            if (cacheTime == -1) { // 缓存永久有效，空值默认60S有效
                if (sqlResult.isEmpty()) {
                    redisCache.setCacheObject(cacheId, sqlResult, cacheEmptyTime, TimeUnit.SECONDS);
                } else {
                    redisCache.setCacheObject(cacheId, sqlResult);
                }
            } else {
                if (sqlResult.isEmpty()) {
                    redisCache.setCacheObject(cacheId, sqlResult, cacheEmptyTime, TimeUnit.SECONDS);
                } else {
                    redisCache.setCacheObject(cacheId, sqlResult, cacheTime, TimeUnit.SECONDS);
                }
            }
            return sqlResult;
        } catch (SQLException | IllegalAccessException e) {
            log.error("executeTaskByModel occur exception", e);
            throw new RuntimeException(e.getMessage());
        } finally {
            try {
                if (databaseToolTaskManage != null) {
                    databaseToolTaskManage.close();
                }
            } catch (SQLException e) {
                log.error("executeTaskByModel occur exception when close source", e);
                throw new RuntimeException(e.getMessage());
            }
        }

    }
}
